import os
import random
import secrets
import time
import uuid

from flask import Flask, request, jsonify
from flask_cors import CORS
import pymysql
from pymysql.cursors import DictCursor
import hashlib
import json as JSON
from flask_mail import Mail, Message
import logging
from http import HTTPStatus
from functools import wraps
from X1_http import checklen, getText, get_answer
from util import DBCommon

# 配置日志记录
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('auth.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# 安全相关配置
MAX_LOGIN_ATTEMPTS = 5
LOGIN_COOLDOWN_SECONDS = 60
TOKEN_EXPIRATION_SECONDS = 3600

# 登录尝试记录 (实际应用中建议使用Redis等缓存)
login_attempts = {}



app = Flask(__name__)
CORS(app)
# 数据库配置
DB_CONFIG = {
    'host': '47.113.150.140',
    'port': 8081,
    'user': 'root',
    'password': 'root',
    'database': 'wangyutong',
    'charset': 'utf8mb4',
    'cursorclass': DictCursor
}

# 配置邮件服务器
app.config['MAIL_SERVER'] = 'smtp.163.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = '18723745291@163.com'
app.config['MAIL_PASSWORD'] = 'UWTNJiNxiVGC7fTX'
app.config['MAIL_DEFAULT_SENDER'] = ('青春志愿服务网', '18723745291@163.com')

mail = Mail(app)


@app.route('/api/register/email', methods=['POST'])
def register_with_email():
    """仅通过邮箱注册（自动生成密码）"""
    data = request.get_json()
    # 解析请求数据（兼容不同格式的传参）
    if isinstance(data, str):
        data = JSON.loads(data)
    email = data.get('email')

    # 校验邮箱是否存在
    if not email:
        return jsonify({'errorMsg': '请传入邮箱'}), 205

    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                # 检查邮箱是否已注册
                sql = "SELECT * FROM w_user WHERE email = %s"
                cursor.execute(sql, (email,))
                if cursor.fetchone():
                    return jsonify({'errorMsg': '邮箱已注册'}), 205
                import string
                characters = string.ascii_letters + string.digits
                random_pwd = ''.join(random.choice(characters) for _ in range(8))
                pwd_hash = hashlib.sha256(random_pwd.encode('utf-8')).hexdigest()
                name = email.split('@')[0]
                sql = "INSERT INTO w_user (name, email, pwd) VALUES (%s, %s, %s)"
                cursor.execute(sql, (name, email, pwd_hash))
                conn.commit()
                user_id = cursor.lastrowid
                msg = Message(
                    subject='青春志愿服务网注册成功',
                    recipients=[email],
                    html=f'''
                        <p>您已成功注册青春志愿服务网，初始密码为：<strong>{random_pwd}</strong></p>
                        <p>请尽快登录并修改密码以保障账号安全。</p>
                    '''
                )
                mail.send(msg)

        return jsonify({
            'status': 'success',
            'message': '注册成功，初始密码已发送至邮箱',
            'userId': user_id
        }), 201

    except Exception as e:
        return jsonify({'errorMsg': f'注册失败：{str(e)}'}), 207

@app.route('/send_email', methods=['POST'])
def send_email():
    data = request.get_json()
    if not data or 'email' not in data:
        print('邮箱没有传入')
        return jsonify({
            'status': 'error',
            'message': '邮箱没有传入'
        }), 205

    recipient = data['email']
    scene = data.get('scene','rest_pwd')

    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                if scene == 'register':
                    sql = "select * from w_user where email = %s"
                    cursor.execute(sql, (recipient,))
                    if cursor.fetchone():
                        return jsonify({'errorMsg': '邮箱已注册!'}), 205
                if scene =='reset_pwd':
                    sql ="select * from w_user where email = %s"
                    cursor.execute(sql,(recipient,))
                    if not cursor.fetchone():
                        return jsonify({'errorMsg':'邮箱未注册'}),
                sql = "select * from w_email where email = %s and createTime >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)"
                cursor.execute(sql, (recipient,))
                res = cursor.fetchone()
                print('查询结果',res)
                if res:
                    print('5分钟以内的记录已存在')
                    code = res['code']
                else:
                    code = ''.join([str(random.randint(0,9)) for _ in range(6)])
                    sql = "insert INTO w_email (id,email,code) VALUES (null,%s,%s)"
                    cursor.execute(sql, (recipient,code))
                    conn.commit()
        if scene == 'reset_pwd':
            msg = Message(
                subject='青年志愿验证码-重置密码验证码',
                recipients=[recipient],
                html=f'<p>您正在重置验证码，您的验证码是：<strong>{code}</strong>，5 分钟内有效</p>' )
        else :
            msg = Message(
                subject='青年志愿验证码',
                recipients=[recipient],
                html=f'<p>您的验证码是：<strong>{code}</strong>，5分钟内有效</p>'
            )
        mail.send(msg)
        print('验证码已发送')
        return jsonify({
            'status': 'success',
            'message': '验证码已发送'
        }), 200

    except Exception as e:
        print('邮件发送失败', str(e))
        return jsonify({
            'status': 'error',
            'message': f'邮件发送失败: {str(e)}'
        }), 207
def get_db_connection():
    """获取数据库连接"""
    return pymysql.connect(**DB_CONFIG)


@app.route('/api/users', methods=['POST'])
def gew_users():
    """获取所有用户"""
    data = request.get_json()
    # data = JSON.loads(data.get('data'))
    print(f'接收到的请求: {data}')
    name = data.get('name')
    email = data.get('email')
    pwd = data.get('pwd')
    pwdConfirm = data.get('pwdConfirm')
    code = data.get('code')
    # 校验pwd和pwdConfirm是否一致
    if pwd != pwdConfirm:
        return jsonify({'errorMsg': '两次输入的密码不一致'}), 205
    if len(pwd) < 6:
        return jsonify({'errorMsg': '密码长度不能少于6位'}), 205

    if not name or not email or not pwd or not pwdConfirm or not code:
        return jsonify({'errorMsg': '缺少必要参数'}), 205

    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                # 检查邮箱是否已存在
                sql = "SELECT * FROM w_user WHERE email = %s"
                cursor.execute(sql, (email,))
                if cursor.fetchone():
                    return jsonify({'errorMsg': '邮箱已被注册'}), 205
                # 检查验证码是否存在，且在5分钟以内
                sql = "SELECT * FROM w_email WHERE email = %s AND code = %s AND createTime >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)"
                cursor.execute(sql, (email, code))
                if not cursor.fetchone():
                    return jsonify({'errorMsg': '验证码无效或已过期'}), 205

                # 密码加密存储（使用SHA-256）
                pwd_hash = hashlib.sha256(pwd.encode('utf-8')).hexdigest()

                sql = "INSERT INTO w_user (name, email, pwd) VALUES (%s, %s, %s)"
                cursor.execute(sql, (name, email, pwd_hash))
                conn.commit()
                user_id = cursor.lastrowid
        return jsonify({'message': '用户创建成功', 'id': user_id}), 201
    except pymysql.Error as e:
        return jsonify({'errorMsg': f'用户创建失败: {str(e)}'}), 205


def rate_limit(f):
    """登录限流装饰器"""

    @wraps(f)
    def decorated(*args, **kwargs):
        client_ip = request.remote_addr
        # 检查是否在冷却期
        if client_ip in login_attempts:
            attempts, last_attempt = login_attempts[client_ip]
            if attempts >= MAX_LOGIN_ATTEMPTS:
                cooldown_remaining = LOGIN_COOLDOWN_SECONDS - (time.time() - last_attempt)
                if cooldown_remaining > 0:
                    return jsonify({
                        'errorMsg': f'登录尝试过多，请等待{int(cooldown_remaining)}秒后再试'
                    }), HTTPStatus.TOO_MANY_REQUESTS

        # 更新登录尝试记录
        if client_ip not in login_attempts:
            login_attempts[client_ip] = [1, time.time()]
        else:
            attempts, last_attempt = login_attempts[client_ip]
            login_attempts[client_ip] = [attempts + 1, time.time()]

        result = f(*args, **kwargs)

        # 登录成功时重置尝试次数
        if result[1] == HTTPStatus.OK:
            if client_ip in login_attempts:
                del login_attempts[client_ip]

        return result

    return decorated


def sanitize_input(data):
    """清理输入数据，防止特殊字符引发问题"""
    if isinstance(data, dict):
        return {k: sanitize_input(v) for k, v in data.items()}
    elif isinstance(data, list):
        return [sanitize_input(v) for v in data]
    elif isinstance(data, str):
        return data.encode('ascii', 'replace').decode('ascii')
    return data


@app.route('/api/login', methods=['POST'])
@rate_limit
def login():
    try:
        # 获取并验证JSON数据
        data = request.get_json()
        if not data:
            logger.warning('无效的JSON请求')
            return jsonify({'errorMsg': '无效的请求格式'}), HTTPStatus.BAD_REQUEST

        # 清理并记录请求数据
        safe_data = sanitize_input(data)
        logger.info(f'接收到登录请求: {safe_data}')

        # 提取并验证必要字段
        email = data.get('email')
        pwd = data.get('pwd')

        if not email or not pwd:
            logger.warning('缺少必要参数')
            return jsonify({'errorMsg': '缺少邮箱或密码'}), HTTPStatus.BAD_REQUEST

        # 防止密码长度攻击
        if len(pwd) > 128:
            logger.warning('密码长度异常')
            return jsonify({'errorMsg': '无效的密码'}), HTTPStatus.BAD_REQUEST

        try:
            with get_db_connection() as conn:
                with conn.cursor() as cursor:
                    # 使用更安全的密码哈希 (建议使用argon2或bcrypt)
                    pwd_hash = hashlib.sha256(pwd.encode('utf-8')).hexdigest()
                    # d35f230112a761a61f20a6c2a3651eb5780f23b407abd0766081464d95fa4b4f
                    logger.info(f'pwd_hash: {pwd_hash}')
                    # d35f230112a761a61f20a6c2a3651eb5780f23b407abd0766081464d95fa4b4f

                    # 查询用户
                    sql = "SELECT id, email, name FROM w_user WHERE email = %s AND pwd = %s"
                    cursor.execute(sql, (email, pwd_hash))
                    user = cursor.fetchone()
                    logger.info(f'email, pwd_hash:{email, pwd_hash}')
                    logger.info(f'user:{user}')
                    if user:
                        # # 生成安全令牌
                        # auth_token = secrets.token_urlsafe(32)
                        #
                        # # 更新用户最后登录信息 (可选)
                        # update_sql = "UPDATE w_user SET last_login=NOW(), auth_token=%s WHERE id=%s"
                        # cursor.execute(update_sql, (auth_token, user['id']))
                        # conn.commit()

                        # 准备返回数据
                        user_info = {
                            'id': user['id'],
                            'email': user['email'],
                            'name': user['name'],
                        }

                        logger.info(f'用户 {email} 登录成功')

                        get_sessions(user['id'])

                        return jsonify({
                            'message': '登录成功',
                            'user': user_info
                        }), HTTPStatus.OK
                    else:
                        logger.warning(f'用户 {email} 登录失败：凭证无效')
                        return jsonify({'errorMsg': '邮箱或密码错误'}), HTTPStatus.UNAUTHORIZED

        except pymysql.Error as e:
            logger.error(f'数据库错误: {str(e)}')
            return jsonify({'errorMsg': '服务器内部错误'}), HTTPStatus.INTERNAL_SERVER_ERROR

    except Exception as e:
        logger.exception('处理登录请求时发生意外错误')
        return jsonify({'errorMsg': '服务器内部错误'}), HTTPStatus.INTERNAL_SERVER_ERROR

# @app.route('/api/login', methods=['POST'])
# def login():
#     data = request.get_json()
#     print(f'接收到的请求: {data}')
#     email = data.get('email')
#     pwd = data.get('pwd')
#     print(f'邮箱: {email}, 密码: {pwd}')
#
#     if not email or not pwd:
#         print('缺少邮箱或密码')
#         return jsonify({'errorMsg': '缺少邮箱或密码'}), 205
#
#     try:
#         with get_db_connection() as conn:
#             with conn.cursor() as cursor:
#                 # 计算密码哈希值
#                 pwd_hash = hashlib.sha256(pwd.encode('utf-8')).hexdigest()
#                 print('开始查询数据库',pwd_hash)
#
#                 sql = "SELECT * FROM w_user WHERE email = %s AND pwd = %s"
#                 cursor.execute(sql, (email, pwd_hash))
#                 user = cursor.fetchone()
#
#         if user:
#             # 登录成功，返回用户信息（不包含密码）
#             user.pop('pwd')
#             print('登录成功')
#             return jsonify({
#                 'message': '登录成功',
#                 'user': user
#             }), 200
#         else:
#             print('邮箱或密码错误')
#             return jsonify({'errorMsg': '邮箱或密码错误'}), 206
#
#     except pymysql.Error as e:
#         print(f'数据库错误: {str(e)}')
#         return jsonify({'errorMsg': f'数据库错误: {str(e)}'}), 207
#
#
# @app.route('/api/reset_password', methods=['POST'])
# def reset_password():
#     """重置密码接口：接收email、code、new_pwd参数"""
#     data = request.get_json()
#     if isinstance(data, str):
#         data = JSON.loads(data)
#
#     email = data.get('email')
#     code = data.get('code')
#     new_pwd = data.get('new_pwd')
#     confirm_pwd = data.get('confirm_pwd')
#
#     # 参数校验
#     if not all([email, code, new_pwd, confirm_pwd]):
#         return jsonify({'errorMsg': '缺少必要参数'}), 205
#     if new_pwd != confirm_pwd:
#         return jsonify({'errorMsg': '两次输入的密码不一致'}), 205
#     if len(new_pwd) < 6:
#         return jsonify({'errorMsg': '密码长度不能少于6位'}), 205
#
#     try:
#         with get_db_connection() as conn:
#             with conn.cursor() as cursor:
#                 # 1. 验证验证码是否有效（5分钟内且匹配）
#                 sql = """
#                     SELECT * FROM w_email
#                     WHERE email = %s AND code = %s
#                     AND createTime >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)
#                 """
#                 cursor.execute(sql, (email, code))
#                 if not cursor.fetchone():
#                     return jsonify({'errorMsg': '验证码无效或已过期'}), 205
#
#                 # 2. 验证邮箱是否已注册
#                 sql = "SELECT * FROM w_user WHERE email = %s"
#                 cursor.execute(sql, (email,))
#                 if not cursor.fetchone():
#                     return jsonify({'errorMsg': '邮箱未注册'}), 205
#
#                 # 3. 加密并更新密码
#                 pwd_hash = hashlib.sha256(new_pwd.encode('utf-8')).hexdigest()
#                 sql = "UPDATE w_user SET pwd = %s WHERE email = %s"
#                 cursor.execute(sql, (pwd_hash, email))
#                 conn.commit()
#
#         return jsonify({
#             'status': 'success',
#             'message': '密码重置成功，请登录'
#         }), 200
#
#     except Exception as e:
#         return jsonify({'errorMsg': f'重置失败：{str(e)}'}), 207

@app.route('/api/users', methods=['GET'])
def gew_users1():
    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                sql = "SELECT id, name, email FROM w_user"  # 不返回密码
                cursor.execute(sql)
                users = cursor.fetchall()
        return jsonify(users), 200
    except pymysql.Error as e:
        return jsonify({'errorMsg': f'数据库错误: {str(e)}'}), 207

@app.route('/api/users/<int:user_id>', methods=['GET'])
def gew_user(user_id):
    """获取单个用户"""
    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                sql = "SELECT id, name, email FROM w_user WHERE id = %s"
                cursor.execute(sql, (user_id,))
                user = cursor.fetchone()

        if not user:
            return jsonify({'errorMsg': '用户不存在'}), 206
        return jsonify(user), 200
    except pymysql.Error as e:
        return jsonify({'errorMsg': f'数据库错误: {str(e)}'}), 207

# 更新用户
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
    data = request.get_json()
    name = data.get('name')
    email = data.get('email')
    pwd = data.get('pwd')

    if not any([name, email, pwd]):
        return jsonify({'errorMsg': '至少需要提供一个更新字段'}), 205

    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                sql = "SELECT * FROM w_user WHERE id = %s"
                cursor.execute(sql, (user_id,))
                user = cursor.fetchone()
                if not user:
                    return jsonify({'errorMsg': '用户不存在'}), 206

                set_clause = []
                params = []
                if name:
                    set_clause.append("name = %s")
                    params.append(name)
                if email:
                    sql = "SELECT * FROM w_user WHERE email = %s AND id != %s"
                    cursor.execute(sql, (email, user_id))
                    if cursor.fetchone():
                        return jsonify({'errorMsg': '该邮箱已被其他用户使用'}), 205
                    set_clause.append("email = %s")
                    params.append(email)
                if pwd:
                    # 密码加密更新
                    pwd_hash = hashlib.sha256(pwd.encode('utf-8')).hexdigest()
                    set_clause.append("pwd = %s")
                    params.append(pwd_hash)
                params.append(user_id)

                sql = f"UPDATE w_user SET {', '.join(set_clause)} WHERE id = %s"
                cursor.execute(sql, params)
                conn.commit()
        return jsonify({'message': '用户更新成功'}), 200
    except pymysql.Error as e:
        return jsonify({'errorMsg': f'数据库错误: {str(e)}'}), 207

@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                sql = "SELECT * FROM w_user WHERE id = %s"
                cursor.execute(sql, (user_id,))
                user = cursor.fetchone()
                if not user:
                    return jsonify({'errorMsg': '用户不存在'}), 206

                sql = "DELETE FROM w_user WHERE id = %s"
                cursor.execute(sql, (user_id,))
                conn.commit()
        return jsonify({'message': '用户删除成功'}), 200
    except pymysql.Error as e:
        return jsonify({'errorMsg': f'数据库错误: {str(e)}'}), 207


def getChat(conventionId):
    """查询指定会话的历史聊天记录"""
    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                # 查询该会话下的所有聊天记录，按时间排序
                cursor.execute(
                    """SELECT role, content, createTime 
                       FROM w_chat 
                       WHERE conventionId = %s 
                       ORDER BY createTime  ASC""",
                    (conventionId,)
                )
                # 获取结果并转换为列表（每条记录包含角色、内容、时间）
                chat_history = cursor.fetchall()
                return chat_history if chat_history else []
    except Exception as e:
        print(f"查询聊天记录失败: {str(e)}")
        return []

def saveChat(user_id, role, content, conventionId, title=""):
    """保存聊天记录到数据库"""
    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                # 检查会话是否已存在
                cursor.execute(
                    "SELECT id FROM w_chat_convention_new WHERE id = %s",
                    (conventionId,)
                )
                if not cursor.fetchone() and title:
                    # 创建新会话
                    cursor.execute(
                        "INSERT INTO w_chat_convention_new (id, user_id, title) VALUES (%s, %s, %s)",
                        (conventionId, user_id, title)
                    )

                # 保存聊天内容
                cursor.execute(
                    """INSERT INTO w_chat (conventionId, user_id, role, content, create_time) 
                       VALUES (%s, %s, %s, %s, NOW())""",
                    (conventionId, user_id, role, content)
                )
                conn.commit()
    except Exception as e:
        print(f"保存聊天记录失败: {str(e)}")

# @app.route('/api/sessions', methods=['GET'])
def get_sessions(user_id):
    logger.info('get_sessions')
    # user_id = request.args.get('userId')
    if not user_id:
        return jsonify({'errorMsg': '用户ID缺失'}), 400
    try:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute(
                    "SELECT id, title, create_time FROM w_chat_convention_new WHERE user_id = %s ORDER BY create_time DESC",
                    (user_id,)
                )
                # sessions = cursor.fetchall()
                # return jsonify(sessions), 200
    except Exception as e:
        return jsonify({'errorMsg': f'查询会话失败: {str(e)}'}), 500

# 添加一个大模型会话接口，传入参数会话id默认为空，如果不为空就查询历史记录，为空就新建一个会话，再传一个参数content，content是用户输入的内容，返回一个json，包含id和content，id是会话id，content是模型返回的内容
@app.route('/api/chat', methods=['POST'])
def apiChat():
    try:
        data = request.get_json()
        print(f'接收到的请求: {data}')
        content = data.get('content')
        user = data.get('user')
        conventionId = data.get('conventionId')
        print(f'接收到的请求data1: {content}')
        print(f'user: {user}')
        print(f'conventionId: {conventionId}')
        print(f"会话ID: {conventionId}")
        print(f"用户输入内容: {content}")
        if not content or not user:
            return jsonify({'errorMsg': '缺少必要参数（内容或用户信息）'}), 400

            # 解析用户信息（前端可能传入JSON字符串）
        if isinstance(user, str):
            try:
                user_info = JSON.loads(user)  # 统一使用user_info变量
            except JSON.JSONDecodeError:
                return jsonify({'errorMsg': '用户信息格式错误'}), 400
        else:
            user_info = user  # 如果已经是字典则直接使用

            # 校验用户ID是否存在
        if not user_info.get('id'):
            return jsonify({'errorMsg': '用户ID不存在'}), 400

            # 处理会话ID（为空则新建会话）
        title = ""
        if not conventionId:
            # 新建会话时，用用户输入的前20个字符作为标题
            title = content[:20] if len(content) > 20 else content
            conventionId = str(uuid.uuid4())  # 生成UUID作为新会话ID

        # 保存用户的消息到数据库
        saveChat(
            user_id=user_info['id'],
            role="user",
            content=content,
            conventionId=conventionId,
            title=title
        )

        # 获取该会话的历史记录（用于上下文关联）
        chat_history = getChat(conventionId)
        print(f'历史会话记录: {chat_history}')

        # 构建包含历史对话的提问内容
        messages = [{"role": msg["role"], "content": msg["content"]} for msg in chat_history]
        # 追加当前用户输入
        messages.append({"role": "user", "content": content})

        def generate_ai_response():
            ai_response = ""
            for chunk in get_answer(messages):
                if chunk["type"] in ["start", "content"]:
                    ai_response += chunk["content"]
                    # 向前端发送内容片段
                    # yield f"data: {json.dumps({'chunk': chunk['content'], 'conventionId': conventionId})}\n\n"
                elif chunk["type"] == "start":
                    # 发送开始标记，可用于前端清空之前的AI回复
                    ai_response += chunk["content"]
                    # yield f"data: {json.dumps({'start': True, 'chunk': chunk['content'], 'conventionId': conventionId})}\n\n"

        # question = ""
        # for msg in chat_history:
        #     question += f"{msg['role']}: {msg['content']}\n"

        # 调用星火大模型获取回复
        #
        ai_response = ""  # 初始化空字符串
        for chunk in get_answer(messages):
            ai_response += chunk["content"]
            # ai_response = get_answer(question)
        print(f'大模型回复: {ai_response}')

        # 保存AI的回复到数据库
        saveChat(
            user_id=user_info['id'],
            role="assistant",
            content=ai_response,
            conventionId=str(uuid.uuid4())
        )

        # 返回结果给前端
        return jsonify({
            'content': ai_response,
            'conventionId': conventionId
        }), 200

    except Exception as e:
        print(f'对话处理错误: {str(e)}')
        return jsonify({'errorMsg': f'处理失败: {str(e)}'}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9000, debug=True)